#include "../include/CentralCache.h"
#include "../include/PageCache.h"
CentralCache CentralCache::_sInstCentalCache;

size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t n, size_t size)
{
	//先算一下要哪一个桶的对象
	size_t index = GetObjSizeClass::Index(size);
	//在要之前先加桶锁
	_spanLists[index].GetMutex().lock();
	//要一个span链表，链表中的对象节点数量未知，可能比n少
	Span *span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);

	//从span链表中要n个对象，如果不够n个，有多少拿多少
	//因为上一层调用只需要1个，因此就算不够n个也没关系
	start = span->_freeList;
	end = start;
	int i = 0;
	//实际获取链表中多少个节点
	size_t actualGetNum = 1;
	while (i < n - 1 && GetNextObj(end) != nullptr)
	{
		// end不断往后找节点，直到获取到n，或者不够n个
		end = GetNextObj(end);
		++i;
		++actualGetNum;
	}
	//切断_freeList
	span->_freeList = GetNextObj(end);
	GetNextObj(end) = nullptr;

	span->_useCount += actualGetNum;

	////条件断点
	// void* cur = start;
	// int j = 0;
	// while (cur)
	//{
	//	cur = GetNextObj(cur);
	//	j++;
	// }
	// if (actualGetNum != j)
	//{
	//	int x = 0;
	// }

	_spanLists[index].GetMutex().unlock();

	return actualGetNum;
}

//获取一个非空的span
Span *CentralCache::GetOneSpan(SpanList &list, size_t size)
{
	//查看当前spanList中有没有还没分配的span对象
	Span *it = list.Begin();
	while (it != list.End())
	{
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}
	//先把桶锁解掉，这样如果其他线程释放内存对象回来，就不会阻塞
	list.GetMutex().unlock();

	//走到这里说明spanList没有对象，需要向下一层PageCache要
	//在要的时候给PageCache加锁
	PageCache::GetInstance()->GetMutex().lock();
	Span *span = PageCache::GetInstance()->NewSpan(GetObjSizeClass::NumMovePage(size));
	span->_isUse = true;
	span->_objSize = size;
	PageCache::GetInstance()->GetMutex().unlock();

	//对获取的span进行切分不需要加锁，因为其他线程在此时访问不到span

	//拿到一个大的span后要进行切分
	//拿到起始地址和内存的字节数大小
	char *spanAddressStart = (char *)(span->_pageId << PAGE_SHIFT);
	size_t spansSize = span->_n << PAGE_SHIFT;
	void *spanAddressEnd = spanAddressStart + spansSize;
	//将span切分成链表，先切一个作为头，然后不断尾插
	span->_freeList = spanAddressStart;
	spanAddressStart += size;
	void *tail = span->_freeList;
	while (spanAddressStart < spanAddressEnd)
	{
		GetNextObj(tail) = spanAddressStart;
		tail = spanAddressStart;
		spanAddressStart += size;
	}
	//最后一个尾结点的next要被置为空
	GetNextObj(tail) = nullptr;

	////条件断点
	// void* cur = (void*)span->_freeList;
	// int j = 0;
	// while (cur)
	//{
	//	cur = GetNextObj(cur);
	//	j++;
	// }
	// if (spansSize/size != j)
	//{
	//	int x = 0;
	// }

	//头插到桶中，需要加锁，因为其他线程有可能正在从桶里拿或者放节点
	list.GetMutex().lock();
	list.PushFront(span);

	return span;
}

//将一定数量的对象释放到span
void CentralCache::ReleaseListToSpans(void *start, size_t size)
{
	//定位这段链表在CentralCache哪个桶
	size_t index = GetObjSizeClass::Index(size);
	_spanLists[index].GetMutex().lock();
	while (start != nullptr)
	{
		void *next = GetNextObj(start);
		//找到对应的Span然后头插
		Span *span = PageCache::GetInstance()->MapObjectToSpan(start);
		GetNextObj(start) = span->_freeList;
		span->_freeList = start;
		--span->_useCount;
		//等于0说明span切分出去的所有小块都回来了
		//这个span就可以回收给PageCache
		// PageCache拿到后可以尝试做前后页的合并
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;
			//在释放时将桶锁解除，否则其他线程无法向桶中申请span
			_spanLists[index].GetMutex().unlock();

			//在还给下层时进行加锁
			PageCache::GetInstance()->GetMutex().lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->GetMutex().unlock();

			_spanLists[index].GetMutex().lock();
		}
		start = next;
	}
	_spanLists[index].GetMutex().unlock(); //解锁
}